<?php
if (! defined('IN_SITE')) {
	exit('invalid request');
}

/**
 * 数据库包装类。
 */
class Db {
	private static $pdo;

	static public function getPDO()
	{
		if (self::$pdo == null) {
			self::$pdo = new PDO(
				$GLOBALS['config']['db_conn'],
				$GLOBALS['config']['db_user'],
				$GLOBALS['config']['db_password'],
				array(PDO::ATTR_PERSISTENT => true)
			);
		}
		return self::$pdo;
	}

	static public function tname($tbl)
	{
		return $GLOBALS['config']['db_prefix'] . $tbl;
	}

	static public function makeSqlValue($value, $bEscape = true)
	{
		if (is_null($value)) {
			$value = 'NULL';
		} else if (is_bool($value)) {
			$value = $value ? 1 : 0;
		} else if ($bEscape) {
			$value = '\'' . mysql_escape_string($value) . '\'';
		} else {
			$value = '\'' . $value . '\'';
		}
		return $value;
	}

	/**
	 * 执行指定的 SQL 语句，返回执行结果。
	 *
	 * @param string $sql
	 * @return mixed 定义如下：
	 *         如果执行的是 SELECT 语句，则返回结果集
	 *         如果执行的是 INSERT/UPDATE/DELETE，则返回 rowCount 值
	 */
	static public function execute($sql)
	{
		$pdo = self::getPDO();

		$sql = trim($sql);
		if (strtoupper(substr($sql, 0, 6)) === 'SELECT') {
			$stmt = $pdo->query($sql);
			if ($stmt === FALSE) {
				$ret = FALSE;
			} else {
				$ret = $stmt->fetchAll(PDO::FETCH_ASSOC);
			}
		} else {
			$ret = $pdo->exec($sql);
		}

		return $ret;
	}

	/**
	 * 把指定的数据行插入数据表，如果是重复记录则放弃。
	 *
	 * @param string $tblname
	 *        	数据表名
	 * @param array $row
	 *        	数据记录
	 * @return 成功时返回影响的记录数（0 或者 1），失败时返回 FALSE
	 */
	static public function insertOrDiscard($tblname, $row)
	{
		$keys = array();
		$values = array();
		foreach ($row as $key => $value) {
			$keys[] = "`{$key}`";
			$values[] = self::makeSqlValue($value);
		}

		$insertkeys = implode(',', $keys);
		$insertvalues = implode(',', $values);
		$sql = "INSERT IGNORE INTO `{$tblname}` ({$insertkeys}) VALUES ({$insertvalues})";

		return self::execute($sql);
	}

	/**
	 * 把指定的数据行插入数据表，如果是重复记录则覆盖。
	 *
	 * @param string $tblname
	 *        	数据表名
	 * @param array $row
	 *        	数据记录
	 * @return 成功时返回影响的记录数（0 或者 1），失败时返回 FALSE
	 */
	static public function insertOrReplace($tblname, $row)
	{
		$keys = array();
		$values = array();
		foreach ($row as $key => $value) {
			$keys[] = "`{$key}`";
			$values[] = self::makeSqlValue($value);
		}

		$insertkeys = implode(',', $keys);
		$insertvalues = implode(',', $values);
		$sql = "REPLACE INTO `{$tblname}` ({$insertkeys}) VALUES ({$insertvalues})";

		return self::execute($sql);
	}
	
	
	/**
	 * 把批量数据行插入数据表，如果是重复记录则覆盖。
	 *
	 * @param string $tblname
	 *        	数据表名
	 * @param array $row
	 *        	数据记录
	 * @return 成功时返回影响的记录数（0 或者 1），失败时返回 FALSE
	 */
	static public function batchInsertOrReplace($tblname,$fields ,$rows)
	{
		$keys = [];
		foreach ($fields AS $v){
			$keys[] = "`{$v}`";
		}
		$insertkeys = implode(',', $keys);
		$all = [];
		$str = '';
		foreach ($rows AS $i=>$value){
			$val = [];
			$str = '(';
			foreach ($value AS $d){
				$val[] = self::makeSqlValue($d);
			}
			$str .= implode(',', $val);
			$str.=')';
			$all[] = $str;
		}
		$insertvalue = implode(',', $all);
		$sql = "REPLACE INTO `{$tblname}` ({$insertkeys}) VALUES $insertvalue";
		return self::execute($sql);
	}

	/**
	 * 把指定的数据行插入数据表，如果是重复记录则更新指定的字段。
	 *
	 * @param string $tblname
	 *        	数据表名
	 * @param array $row
	 *        	数据记录
	 * @param string $update_clause
	 *        	当记录重复时需按此更新。此字符串将直接嵌入 SQL 语句，需注意“SQL 注入”问题！
	 * @return 成功时返回影响的记录数（0 或者 1），失败时返回 FALSE
	 */
	static public function insertOrUpdate($tblname, $row, $update_clause)
	{
		$keys = array();
		$values = array();
		foreach ($row as $key => $value) {
			$keys[] = "`{$key}`";
			$values[] = self::makeSqlValue($value);
		}

		$insertkeys = implode(',', $keys);
		$insertvalues = implode(',', $values);

		$sql = "INSERT INTO `{$tblname}` ({$insertkeys}) VALUES({$insertvalues}) ON DUPLICATE KEY UPDATE {$update_clause}";
		return self::execute($sql);
	}

	/*
	 * 从给定的查询条件来构建字符串形式的 WHERE 子句。
	 * 支持以下几种条件格式： ISNULL(`n_col`) AND `i_col`=123 AND `s_col`='abc' AND `x_col` IN (1, 'a')
	 * opt 支持以下项目： table_alias
	 */
	public static function buildWhereClause($criteria, $bEscape = true, $opt = null)
	{
		if (empty($criteria)) {
			return '';
		}

		$table_prefix = empty($opt['table_alias']) ? '' : "`{$opt['table_alias']}`.";

		$conds = array();
		foreach ($criteria as $key => $value) {
			if (is_null($value)) {
				$conds[] = "ISNULL({$table_prefix}`{$key}`)";
			} else if (is_array($value)) {
				$list = array();
				foreach ($value as $item) {
					if (is_null($item)) {
						// ignore
					} else if (is_int($item)) {
						$list[] = $item;
					} else if ($bEscape) {
						$list[] = "'" . mysql_escape_string($item) . "'";
					} else {
						$list[] = "'{$item}'";
					}
				}
				$conds[] = "{$table_prefix}`{$key}` IN (" . implode(',', $list) . ")";
			} else if (is_int($value)) {
				$conds[] = "{$table_prefix}`{$key}`={$value}";
			} else if ($bEscape) {
				$value = mysql_escape_string($value);
				$conds[] = "{$table_prefix}`{$key}`='{$value}'";
			} else {
				$conds[] = "{$table_prefix}`{$key}`='{$value}'";
			}
		}
		return ' WHERE ' . implode(' AND ', $conds);
	}

	/**
	 * 把指定的数据行更新数据表
	 *
	 * @param string $tblname
	 *        	数据表名
	 * @param array $attrs
	 *        	数据记录
	 * @param string $criteria
	 *        	更新条件
	 * @return 成功时返回影响的记录数（0 或者 1），失败时返回 FALSE
	 */
	static public function update($tblname, $attrs, $criteria)
	{
		$updates = array();
		foreach ($attrs as $key => $value) {
			$value = self::makeSqlValue($value);
			$updates[] = "`{$key}`={$value}";
		}
		$updatesql = implode(',', $updates);

		$where = self::buildWhereClause($criteria);

		$sql = "UPDATE `{$tblname}` SET {$updatesql} {$where}";
		return self::execute($sql);
	}

	/**
	 * 查询多条记录。
	 *
	 * @param string $sql
	 *        	查询语句
	 * @param string $keyfield
	 *        	指定一个字段，该字段的值将作为返回数组的 key
	 * @return array 每个元素是一个 array，表示一条数据记录。
	 */
	static public function queryRows($sql, $keyfield = null)
	{
		$rows = self::execute($sql);
		if (!empty($rows) && !empty($keyfield)) {
			$temp = $rows;
			$rows = array();
			foreach ($temp as $row) {
				$rows[$row[$keyfield]] = $row;
			}
		}
		return $rows;
	}

	/**
	 * 查询单条记录。
	 *
	 * @param string $sql
	 *        	查询语句
	 * @return array 每个元素是一个 array，表示一条数据记录。
	 */
	static public function querySingleRow($sql)
	{
		$rows = self::execute($sql);
		return $rows[0];
	}

	/**
	 * 获取单值列表。
	 *
	 * @param string $sql
	 * @return array 每个元素是一个 string 值。
	 */
	static public function querySingleValueList($sql)
	{
		$list = array();
		$rows = self::execute($sql);
		foreach ($rows as $row) {
			reset($row);
			$list[] = current($row);
		}
		return $list;
	}

	/**
	 * 获取单值。
	 *
	 * @param string $sql
	 * @return string 返回结果集的第一条记录的第一个字段值。
	 */
	static public function querySingleValue($sql)
	{
		$rows = self::execute($sql);
		$row = $rows[0];
		return current($row);
	}

	static public function beginTransaction() { $pdo = self::getPDO(); return $pdo->beginTransaction(); }
	static public function commit() { $pdo = self::getPDO(); return $pdo->commit(); }
	static public function rollBack() { $pdo = self::getPDO(); return $pdo->rollBack(); }
	static public function errorInfo() { $pdo = self::getPDO(); return $pdo->errorInfo(); }
	static public function lastInsertId() { $pdo = self::getPDO(); return $pdo->lastInsertId(); }
}
